package tcp

import (
	"errors"
	"log"
	"net"
	"sync"
	"time"
)

/**
 * @Description
 * @Author : 弘泰通保
 * @Date 2025/8/21 15:50
 **/

// Pool 连接池
type Pool interface {
	// Get 获取连接
	Get() (net.Conn, error)

	// Put 放回连接，不是关闭
	Put(conn net.Conn) error

	// Release 释放池（全部连接）
	Release() error

	// Len 有效连接数
	Len() int
}

// PoolConfig 连接池的配置
type PoolConfig struct {

	// InitConnNum 初始化连接数，至少保持多少个有效连接
	InitConnNum int

	//最大连接数，池中最多支持多少连接
	MaxConnNum int

	//最大空闲连接数，池中最多有多少可用的连接
	MaxIdleNum int

	//空闲连接超时时间，多久后空闲连接会被释放
	IdleTimeout time.Duration

	//连接工厂
	Factory ConnFactory
}

// IdleConn 空闲连接类型 （管理连接）
type IdleConn struct {
	// 连接本身
	conn net.Conn

	// 返回时间
	putTime time.Time
}

type TcpPool struct {
	// 相关配置
	config PoolConfig

	// 运行时信息
	// 使用连接数量
	openingConnNum int

	// 空闲连接数量
	idleList chan *IdleConn

	// 连接地址
	addr string

	// 并发安全锁
	mu sync.RWMutex
}

// ConnFactory 连接工厂
type ConnFactory interface {
	// Factory 构造连接
	Factory(addr string) (net.Conn, error)

	// Close 关闭连接的方法
	Close(net.Conn) error

	// Ping 检查连接是否有效的方法
	Ping(net.Conn) error
}

// TcpConnFactory  Tcp连接工厂类型
type TcpConnFactory struct{}

func (tcpConnFactory *TcpConnFactory) Close(conn net.Conn) error {
	return conn.Close()
}

// Factory 创建连接连接方法
func (tcpConnFactory *TcpConnFactory) Factory(addr string) (net.Conn, error) {
	// 校验参数的合理性
	if addr == "" {
		return nil, errors.New("addr is empty")
	}

	// 建立连接
	conn, err := net.DialTimeout("tcp", addr, 15*time.Second)
	if err != nil {
		return nil, err
	}

	return conn, nil
}

func (tcpConnFactory *TcpConnFactory) Ping(net.Conn) error {
	return nil
}

// Get 获取连接 TcpPool 实现Pool接口
func (tcpPool *TcpPool) Get() (net.Conn, error) {
	// 锁定
	tcpPool.mu.RLock()
	defer tcpPool.mu.RUnlock()

	// 获取空闲连接 没有则创建连接
	for {
		select {
		// 获取空闲连接
		case idleConn, ok := <-tcpPool.idleList:
			// 判断 channel是否被关闭
			if !ok {
				return nil, errors.New("idle list closed")
			}

			// 判断连接是否超时
			//tcpPool.config.IdleTimeout, idleConn.putTime
			if tcpPool.config.IdleTimeout > 0 { // 设置了超时时间
				// putTime + timeOut 是否在 now 之前
				if idleConn.putTime.Add(tcpPool.config.IdleTimeout).Before(time.Now()) {
					// 关闭连接 查找下一个连接
					err := tcpPool.config.Factory.Close(idleConn.conn)
					if err != nil {
						return nil, err
					}
					continue
				}
			}

			// 判断连接是否可用
			if err := tcpPool.config.Factory.Ping(idleConn.conn); err != nil {
				// ping 失败，连接不可用
				// 关闭连接， 继续查找
				_ = tcpPool.config.Factory.Close(idleConn.conn)
				continue
			}

			log.Println("get from idle connection")
			// 找到可用连接的空闲连接，使用的连接计数
			tcpPool.openingConnNum++
			return idleConn.conn, nil
		default:

			// 创建连接
			// 判断是否可以继续创建连接
			// 基于开放的连接是否已经达到了连接池最大的连接数
			if tcpPool.openingConnNum >= tcpPool.config.MaxConnNum {
				return nil, errors.New("max opening connection")
			}

			// 创建连接
			conn, err := tcpPool.config.Factory.Factory(tcpPool.addr)
			if err != nil {
				return nil, err
			}

			log.Println("created max connection factory")
			// 正确创建了可用连接
			// 使用连接计数
			tcpPool.openingConnNum++
			return conn, nil
		}
	}
}

// Put 将连接放回连接池，不是关闭
func (tcpPool *TcpPool) Put(conn net.Conn) error {
	// 并发安全锁
	tcpPool.mu.Lock()
	defer tcpPool.mu.Unlock()

	// 校验操作
	if conn == nil {
		return errors.New("connection is not exists")
	}

	// 判断空闲连接列表是否存在
	if tcpPool.idleList == nil {
		// 关闭连接
		_ = tcpPool.config.Factory.Close(conn)
		return errors.New("idle list is empty")
	}

	// 放回连接
	select {
	case tcpPool.idleList <- &IdleConn{conn: conn, putTime: time.Now()}:
		// 发送成功任务完成
		// 奉新开放连接数量
		tcpPool.openingConnNum--
		return nil

	// 关闭连接
	default:
		_ = tcpPool.config.Factory.Close(conn)
		return nil
	}
}

// Release 释放池（全部连接）
func (tcpPool *TcpPool) Release() error {
	log.Printf("release connection pool")
	// 并发安全锁
	tcpPool.mu.Lock()
	defer tcpPool.mu.Unlock()

	// 确定连接池是否被释放
	if tcpPool.idleList == nil {
		return nil
	}

	// 关闭IdleList
	close(tcpPool.idleList)

	// 释放全部空闲链接
	// 继续接收已关闭channel中的元素
	for idleConn := range tcpPool.idleList {
		// 关闭连接忽略error
		_ = tcpPool.config.Factory.Close(idleConn.conn)
	}

	return nil
}

// Len 连接池可用连接数量
func (tcpPool *TcpPool) Len() int {
	return len(tcpPool.idleList)
}

const (
	defaultMaxConnNum  = 100
	defaultInitConnNum = 1
)

// NewTcpPool 创建 tcpPoll 对象
func NewTcpPool(addr string, poolConfig PoolConfig) (*TcpPool, error) {
	// 校验参数
	if addr == "" {
		return nil, errors.New("addr is empty")
	}
	// 校验工厂的存在
	if poolConfig.Factory == nil {
		return nil, errors.New("factory is not exists")
	}
	// 最大连接数
	if poolConfig.MaxConnNum == 0 {
		poolConfig.MaxConnNum = defaultMaxConnNum
	}
	// 初始化了连接
	if poolConfig.InitConnNum == 0 {
		poolConfig.InitConnNum = defaultInitConnNum
	} else if poolConfig.InitConnNum > poolConfig.MaxConnNum {
		poolConfig.InitConnNum = poolConfig.MaxConnNum
	}
	// 合理化最大连接数
	if poolConfig.MaxIdleNum == 0 {
		poolConfig.MaxIdleNum = poolConfig.InitConnNum
	} else if poolConfig.MaxIdleNum > poolConfig.MaxConnNum {
		poolConfig.MaxIdleNum = poolConfig.MaxConnNum
	}

	//初始化TcpPool对象
	pool := TcpPool{
		config:         poolConfig,
		openingConnNum: 0,
		idleList:       make(chan *IdleConn, poolConfig.MaxConnNum),
		mu:             sync.RWMutex{},
		addr:           addr,
	}

	// 初始化连接
	for i := 0; i < poolConfig.InitConnNum; i++ {
		conn, err := pool.config.Factory.Factory(addr)
		if err != nil {
			err := pool.Release()
			if err != nil {
				return nil, err
			}
			return nil, err
		}
		// 连接成功 加入到空闲连接队列中
		pool.idleList <- &IdleConn{
			conn:    conn,
			putTime: time.Now(),
		}
	}
	return &pool, nil
}
